Skip to content

Conversation

@rlarkdfus
Copy link
Contributor

@rlarkdfus rlarkdfus commented May 22, 2025

Taoshi Pull Request

Description

Pass order time for historic price calls / pass None for live price calls to data services.
Order times are unified to one timestamp for each trade pair.

Related Issues (JIRA)

[Reference any related issues or tasks that this pull request addresses or closes.]

Checklist

  • I have tested my changes on testnet.
  • I have updated any necessary documentation.
  • I have added unit tests for my changes (if applicable).
  • If there are breaking changes for validators, I have (or will) notify the community in Discord of the release.

Reviewer Instructions

[Provide any specific instructions or areas you would like the reviewer to focus on.]

Definition of Done

  • Code has been reviewed.
  • All checks and tests pass.
  • Documentation is up to date.
  • Approved by at least one reviewer.

Checklist (for the reviewer)

  • Code follows project conventions.
  • Code is well-documented.
  • Changes are necessary and align with the project's goals.
  • No breaking changes introduced.

Optional: Deploy Notes

[Any instructions or notes related to deployment, if applicable.]

/cc @mention_reviewer

@deepsource-io
Copy link

deepsource-io bot commented May 22, 2025

Here's the code health analysis summary for commits bb84728..02765d7. View details on DeepSource ↗.

Analysis Summary

AnalyzerStatusSummaryLink
DeepSource Shell LogoShell✅ SuccessView Check ↗
DeepSource Python LogoPython❌ Failure
❗ 28 occurences introduced
🎯 31 occurences resolved
View Check ↗
DeepSource Test coverage LogoTest coverage❌ Failure
❗ 8 occurences introduced
🎯 7 occurences resolved
View Check ↗

Code Coverage Report

MetricAggregatePython
Branch Coverage100%100%
Composite Coverage78.7% (up 0.1% from main)78.7% (up 0.1% from main)
Line Coverage78.7% (up 0.1% from main)78.7% (up 0.1% from main)
New Branch Coverage100%100%
New Composite Coverage28.2%28.2%
New Line Coverage28.2%28.2%

💡 If you’re a repository administrator, you can configure the quality gates from the settings.

@rlarkdfus rlarkdfus force-pushed the fix/historic-prices branch from 777459b to 6727d4f Compare May 23, 2025 00:56

def get_closes_websocket(self, trade_pairs: List[TradePair], trade_pair_to_last_order_time_ms) -> dict[str: PriceSource]:
def get_closes_websocket(self, trade_pairs: List[TradePair], time_ms) -> dict[str: PriceSource]:
events = {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice cleanup!


def time_delta_from_now_ms(self, now_ms: int) -> int:
def time_delta_from_now_ms(self, now_ms:int = None) -> int:
if not now_ms:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice fix. this could have caused errors

)

price_sources = self.live_price_fetcher.get_sorted_price_sources_for_trade_pair(trade_pair, now_ms)
price_sources = self.live_price_fetcher.get_sorted_price_sources_for_trade_pair(trade_pair)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not pass the time from before? It is the closest timestamp to the order received time as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data services check whether or not there is any value assigned to time_ms, so even if the timestamp is right now, it will try to retrieve historical data. It looked like the validator was trying to fetch live data, so to call the same endpoint live endpoint it called before, I passed in None.

Copy link
Collaborator

@jbonilla-tao jbonilla-tao May 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal of this code segment is to get the price closest to the price when the order was received by the validator. That's why the code passed "now_ms" to get_sorted_price_sources_for_trade_pair. By using the current time, it will be a little bit in the future compared to when the order was placed.

bars_df = cls.get_bars_with_features(trade_pair, processed_ms, adv_lookback_window, calc_vol_window)
row_selected = bars_df.iloc[-1]
annualized_volatility = row_selected['annualized_vol']
annualized_volatility = row_selected['annualized_vol'] # recalculate slippage false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsure what this comment means

secrets = ValiUtils.get_secrets() # {'polygon_apikey': '123', 'tiingo_apikey': '456'}
btm = BacktestManager(hk_to_positions, start_time_ms, secrets, None, capital=500_000,
use_slippage=True, fetch_slippage_data=False, recalculate_slippage=False,
use_slippage=True, fetch_slippage_data=True, recalculate_slippage=True, # recalculate slippage fetch slippage data originally false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these changes necessary or is it only for the local testing

@rlarkdfus rlarkdfus force-pushed the fix/historic-prices branch 2 times, most recently from 35ed251 to 6deb4ef Compare July 1, 2025 20:08
@github-actions
Copy link

github-actions bot commented Oct 22, 2025

🤖 Claude AI Code Review

Last reviewed on: 14:23:47

Summary

This PR unifies timestamp handling for historic vs. live price calls across data services. The main changes involve:

  • Replacing trade-pair-specific timestamp maps with a single unified time_ms parameter
  • Introducing a live boolean flag to distinguish between live and historic price fetching
  • Refactoring REST endpoints to accept time_ms and use it consistently
  • Simplifying the crypto price fetching logic in Tiingo by consolidating two different endpoints into one

✅ Strengths

  • Cleaner API: Unified timestamp approach simplifies the interface significantly
  • Good refactoring scope: The changes are focused on a specific issue without unnecessary scope creep
  • Practical approach: Using a boolean live flag is straightforward and understandable
  • Thread safety maintained: Parallel fetching patterns remain intact

⚠️ Concerns

Critical Issues

  1. Breaking API Change Without Clear Migration Path (Lines 644-654 in tiingo_data_service.py)

    def get_close_rest(self, trade_pair: TradePair, timestamp_ms: int, live=True)

    The signature changed from attempting_prev_close and optional target_time_ms to required timestamp_ms. This will break existing callers not updated in this PR.

  2. Inconsistent NULL Handling (Multiple locations)

    # vali_dataclasses/price_source.py:69
    if not now_ms:
        now_ms = TimeUtil.now_in_millis()

    vs.

    # live_price_fetcher.py:102
    if not time_ms:
        time_ms = TimeUtil.now_in_millis()

    The live_price_fetcher.py:102 should use explicit None check (if time_ms is None) since 0 is a valid timestamp.

  3. Incomplete Error Handling (Line 680, tiingo_data_service.py)

    ps = tds.get_close_rest(TradePair.TAOUSD, timestamp_ms=None, live=True)

    Passing None for timestamp_ms when it's typed as int (not Optional[int]) will cause runtime issues.

  4. Test Mock Signature Mismatch (Lines 63-67, mock_classes.py)

    def get_sorted_price_sources_for_trade_pair(self, trade_pair, time_ms=None, live=True):
        return [PriceSource(open=1, high=1, close=1, low=1, bid=1, ask=1)]

    The mock now accepts different parameters but doesn't validate them. Tests may pass while production code fails.

Moderate Issues

  1. Logic Inversion Confusion (Lines 369, 435, 459, 528 in tiingo_data_service.py)

    if not live:  # Actually means "is historic"

    This is readable but inverting boolean logic can be confusing. Consider is_historic or use_historic_data instead.

  2. Removed Fallback Logic Without Explanation (Lines 523-640, tiingo_data_service.py)
    The crypto endpoint completely changed from using top endpoint for live data to always using the prices endpoint. This is a significant behavior change that needs validation:

    • Does the new endpoint have the same latency?
    • Are there rate limit implications?
    • What happens if the new endpoint doesn't return data for very recent timestamps?
  3. Misleading Variable Names (Line 553, tiingo_data_service.py)

    data_time_ms = TimeUtil.parse_iso_to_ms(closest_data["date"]) + timespan_ms

    Adding timespan_ms to align with close price time, but this is not documented and could be confusing.

  4. Deprecated Code Not Removed (Line 607)

    # Previously used for deprecated tiingo top-of-book rest endpoint - not used anymore (06/24/2025)
    def get_best_crypto_price_info(self, book_data, now_ms, preferred_exchange):

    This method should be removed if it's no longer used, not just commented as deprecated.

💡 Suggestions

  1. Type Safety Improvements

    # Before
    def get_closes_rest(self, trade_pairs: List[TradePair], time_ms, live=True) -> dict:
    
    # After
    def get_closes_rest(
        self, 
        trade_pairs: List[TradePair], 
        time_ms: Optional[int] = None,  # None means "now"
        live: bool = True
    ) -> Dict[TradePair, PriceSource]:
  2. Consistent Parameter Naming
    Use either time_ms or timestamp_ms consistently throughout. Currently mixed usage creates confusion.

  3. Add Parameter Validation

    def get_closes_rest(self, trade_pairs: List[TradePair], time_ms: Optional[int] = None, live: bool = True):
        if not live and time_ms is None:
            raise ValueError("time_ms must be provided for historic price queries")
        if live and time_ms is None:
            time_ms = TimeUtil.now_in_millis()
  4. Document the Behavior Change
    Add docstrings explaining the new parameters:

    """
    Fetch closing prices for trade pairs.
    
    Args:
        trade_pairs: List of trade pairs to fetch
        time_ms: Target timestamp in milliseconds. If None and live=True, uses current time.
        live: If True, fetches latest available price. If False, fetches historic price at time_ms.
        
    Returns:
        Dictionary mapping trade pairs to their price sources
    """
  5. Add Logging for Price Source Selection

    if not live:
        bt.logging.debug(f"Fetching historic price for {ticker} at {TimeUtil.millis_to_formatted_date_str(time_ms)}")
  6. Refactor Repeated Code (Line 545-575)
    The PriceSource creation logic is similar across forex/crypto/equities. Consider extracting to a helper method.

  7. Version Notes Should Include Breaking Changes
    In meta/meta.json, the version bump from 7.1.2 to 7.1.3 should be documented as containing breaking API changes (should probably be 7.2.0 or 8.0.0 depending on your versioning strategy).

🔒 Security Notes

  1. No SQL Injection Risk: No database queries modified

  2. API Key Handling: Unchanged, still passed through safely

  3. Input Validation Missing: time_ms parameter is not validated for reasonable ranges. Consider adding:

    if time_ms is not None:
        min_valid_time = 946684800000  # Jan 1, 2000
        max_valid_time = TimeUtil.now_in_millis() + 86400000  # +1 day
        if not (min_valid_time <= time_ms <= max_valid_time):
            raise ValueError(f"Invalid timestamp: {time_ms}")
  4. Thread Safety: The parallel fetching with ThreadPoolExecutor remains safe, no shared state issues introduced

📋 Additional Notes

Testing Concerns:

  • No unit tests added despite the checklist item being "not applicable"
  • This is a significant API change that should have tests
  • The mock objects are updated but don't verify the new behavior
  • Recommend adding tests for:
    • Live price fetching with time_ms=None
    • Historic price fetching with specific timestamp
    • Error cases (invalid timestamps, missing data)

Documentation:

  • The PR description mentions "Order times are unified to one timestamp for each trade pair" but the implementation shows all trade pairs now use the same timestamp
  • This is actually a simplification but should be clearly documented

Deployment Risk:

  • This changes core pricing logic
  • Recommend staged rollout with monitoring
  • The "testnet testing" checkbox is marked but no test results are shared

Recommendation

Conditional Approval - The core approach is sound, but requires fixes before merging:

  1. Fix the type annotations and null handling
  2. Add validation for the time_ms parameter
  3. Remove or properly deprecate unused code
  4. Add basic unit tests
  5. Update documentation with breaking change notice

@sli-tao sli-tao force-pushed the fix/historic-prices branch from 5671c2a to 13bb765 Compare November 24, 2025 04:16
sli-tao added a commit that referenced this pull request Dec 2, 2025
https: //github.com//pull/548
Co-Authored-By: rlarkdfus <77201903+rlarkdfus@users.noreply.github.com>
@sli-tao
Copy link
Contributor

sli-tao commented Dec 2, 2025

Added to #620

@sli-tao sli-tao closed this Dec 4, 2025
jbonilla-tao added a commit that referenced this pull request Dec 5, 2025
* Move live price fetcher to its own process with rpc

* Remove direct references for live price fetcher

* Fix pickle generator bug

* Fix test case

* Add client connection retry and logging

* Refactor to RPC server/client architecture and ServerOrchestrator pattern

Major changes:
- Implemented RPC server/client architecture for all major components
- Added ServerOrchestrator singleton for centralized server lifecycle management
- Migrated servers: LivePriceFetcher, PerfLedger, DebtLedger, ChallengePeriod,
  Elimination, PositionManager, Plagiarism, PlagiarismDetector, AssetSelection,
  LimitOrder, Contract, PositionLock, CoreOutputs, MinerStatistics
- Added health monitoring and watchdog systems for all RPC servers
- Implemented automatic restart and error recovery mechanisms
- Added port management and shutdown coordination
- Refactored test infrastructure to use ServerOrchestrator (50%+ faster test startup)
- Added comprehensive RPC client/server base classes with connection retry logic
- Implemented exponential backoff for failed operations
- Created unified metagraph and common data servers
- Added order processor and market order manager for limit order support
- Enhanced position splitting and lock mechanisms
- Improved test robustness with proper RPC method exposure
- Added account_size parameter to Position for proper leverage validation
- Updated all tests to maintain full robustness with RPC architecture

Technical improvements:
- Multiprocessing architecture for better isolation and stability
- Standardized daemon patterns across all servers
- Comprehensive health checks and monitoring
- Automatic cleanup on process exit
- Thread-safe singleton patterns
- Reduced test execution time by sharing server infrastructure
- Better error handling and logging throughout

* test progress

* fix miner. test progress

* miner fixes. test progress 2

* fix 3

* debug receive signal

* Fix

* Fix dev endpoint

* coldkey-hotkey verification RPC method

* use websocket price fetching flow for quote and base usd conversions

* Reapply order read changes

* Correctly call live/historic prices for rest

https: //github.com//pull/548
Co-Authored-By: rlarkdfus <77201903+rlarkdfus@users.noreply.github.com>

* update live price fetcher rpc method signatures

* Add signal field verifications

* fix position inspector bug

* misc test fixes

* fix

* fix

* fix

* fix

* fixes, tests

* fix payout

* test progress. fix perf ledgers

* no more pickle ledgers. TODO: Remove per-tp ledgers to reduce size. maybe sparse representation

* debug

* more logs

* debug log

* logs

* metagraph robustness. more logs

* more logs

* logs

* logs. priority ledger rebuild

* debt ledger bug fix.

* test progress

* test fix

* Connection cleanup

* limit order daemon fix. test progress

* test checkpoint. daemon centralization

* Fix limit order tests, live price fetcher market patch not working

* tests

* tests

* Fix market open call for test

* tests, dev order api tier 200

* test

* delete unused files

* Cancel order by uuid from miner

* write position inspector positions to file

* Remove cancel by trade pair, only cancel by uuid

* Fix tests

* Fix api key tier for dev

* Validate sltp fields on auto bracket creation from limit fill.

* reorganize repo

* unify order processing

* Fix test

* Validate bracket order after reading position direction

* Add argparse import to emissions_ledger.py

* miner does not require secrets file

* Update sample signal and logs

* Enforce asset selection for promotion

Clear logging

Include challenge period miners in promotion ranking

Validate challenge miners being ranked has ledgers/positions

Clean message

* clean up validator.py and refactor. Use efficient RPC to get asset selection for a single miner

* Include last place miner

* remove copyright symbol causing encoding issues in github ci

* mdd checker fix. logging fix

* fix CI race condition?

* cleanup/refactor/ test determinism

* symbol cause github ci crash

* test determinism

* remove copyright symbol

* fix name

* no client for weight setter

* refactor

* rm dead code

* ensure validator caches are pre-warmed before accepting orders.

* fix price call. add price fetching tests. update debt based scoring to carry over PnL from Nov 1st for future calculations.

* no client for weight setter

* flat order with exact quantity and value but price diff for leverage

* Skip saving limit order that fills immediately on disk

* cleanup

* tiingo batching to avoid 362|vanta  | 2025-12-05 17:08:40.323 |     WARNING      | Tiingo crypto API request failed with status code 400. URL: https://api.tiingo.com/tiingo/crypto/prices?tickers=btcusd,ethusd,solusd,xrpusd,dogeusd,adausd,taous... Response: {"detail":"Error: A limit of 5 tickers may be requested at a time"}

* stop populating websocket data for suspended/blocked trade pairs

* fix tiingo url to be 1 min instead of 5 min. Fix lag calculation. Update tests in mdd checker

---------

Co-authored-by: Jordan Bonilla <jbonilla@taoshi.io>
Co-authored-by: sli-tao <sli@taoshi.io>
Co-authored-by: rlarkdfus <77201903+rlarkdfus@users.noreply.github.com>
Co-authored-by: jbonilla-tao <161871533+jbonilla-tao@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants